package com.eric.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eric.domain.bso.SysMenuBso;
import com.eric.domain.dto.LocationDto;
import com.eric.domain.dto.SysMenuDto;
import com.eric.domain.po.SysMenu;
import com.eric.mapper.SysMenuMapper;
import com.eric.service.ILocationService;
import com.eric.service.ISysMenuService;
import com.eric.utils.PublicTools;
import com.eric.utils.ServerBASEURL;
import com.github.yulichang.base.MPJBaseServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.*;

import static com.eric.utils.Constant.PAGE_CURRENT;
import static com.eric.utils.Constant.PAGE_SIZE;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author 新哥
 * @since 2024-03-12
 */
@Service
@RequiredArgsConstructor
public class  SysMenuServiceImpl extends MPJBaseServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
//    //   定义时间格式
//    SimpleDateFormat sdf=new SimpleDateFormat("/yyyy/MM/dd/");
    @Value("${file.location}")
    String fileLocation;
    String menuIconPath="/menuIcon/";
    private final ILocationService locationService;

    //    获取这个菜单的菜单树
    @Override
    public SaResult getAllMenusService() {
//        从数据库获取全量菜单后放在sysMenus中
        List<SysMenu>sysMenus=query().list();
        for (SysMenu sysMenu : sysMenus) {
            if (sysMenu.getPid()==null){
                return SaResult.error("数据中的pid错误！");
            }
        }
        List<SysMenuDto> sysMenuDtoList = BeanUtil.copyToList(sysMenus, SysMenuDto.class);
        List<SysMenuDto> result= parseMenuTreeNew(sysMenuDtoList,0);

        return SaResult.data(result);
    }

//    根据当前菜单的id，获取该菜单后代菜单的菜单树
    @Override
    public SaResult getTreeMenuByIdService(Integer id) {
        //        从数据库获取全量菜单后放在sysMenus中
        List<SysMenu>sysMenus=query().list();
        for (SysMenu sysMenu : sysMenus) {
            if (sysMenu.getPid()==null){
                return SaResult.error("数据中的pid错误！");
            }
        }
        List<SysMenuDto> sysMenuDtoList = BeanUtil.copyToList(sysMenus, SysMenuDto.class);
        List<SysMenuDto> result= parseMenuTreeNew(sysMenuDtoList,id);

        return SaResult.data(result);
    }


    @Override
    public SaResult getBaseMenuService() {
        //        1、获取登录用户id
        Object id = StpUtil.getLoginId();
        int loginId = Integer.parseInt(id.toString());
//        2、根据登陆者id，获取到登陆者的Location表中对应的仓库和项目

//        发现第一层菜单没有材料管理和设备管理要注意，如果创建的用户，没有分配项目和仓库，这两个菜单就会不显示
//        所以要在Location表中设置好。
        SaResult locationList = locationService.getAllByUserIdService(loginId, 1, 100);
        Object data =locationList.getData();
//        把Object类型的data转换为LocationDto的Array列表，采用自定义的castList方式
        List<LocationDto>locationDtoList = castList(data, LocationDto.class);


//        创建两个列表容器
        List<Integer>warehouseIds=new ArrayList<>();
        List<Integer>projectIds=new ArrayList<>();

        for (LocationDto locationDto : locationDtoList) {
            if (locationDto.getProjectId()>0){
                projectIds.add(locationDto.getProjectId());
            }
            if (locationDto.getWarehouseId()>0){
                warehouseIds.add(locationDto.getWarehouseId());
            }
        }

//        发现第一层菜单没有材料管理和设备管理要注意，如果创建的用户，没有分配项目和仓库，这两个菜单就会不显示
//        所以要在Location表中设置好。

        List<SysMenu> sysMenus = query().eq("pid",2).and(true,j->j.eq("level",2)).list();

        //       如果warehouseIds是空，就把获取到的菜单数据去掉材料管理项
        if (warehouseIds.isEmpty()){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "材料管理"));
        }
//        如果projectIds是空，就把获取到的菜单数据去掉设备管理线
        if (projectIds.isEmpty()){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "设备管理"));
        }
//        获取登录用户的角色
        List<String> roleList = StpUtil.getRoleList();

//

        if (!PublicTools.hasAnyCommonElement(roleList,Arrays.asList("sys:admin","sys:manager","sys:coder","sys:superAdmin"))){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "后台管理"));
        }

        List<SysMenuBso> sysMenuBsoList = BeanUtil.copyToList(sysMenus, SysMenuBso.class);

//         sysMenuBsoList读取数据库中的图标地址+服务器地址，合并成完整的图标地址
        for (SysMenuBso sysMenuBso : sysMenuBsoList) {
            String icoPath = sysMenuBso.getIcoPath();
            if (icoPath!=null){
                sysMenuBso.setIcoPath(ServerBASEURL.BASEURL +icoPath);
            }
        }

        return SaResult.data(sysMenuBsoList);


    }

////    一个集合中任何一个元素在另外一个集合中，即判定为true，否则为false
//    public static <T> boolean hasAnyCommonElement(List<T> list1,List<T> list2){
//        for (T element : list1) {
//            if (list2.contains(element)) {
//                return true;
//            }
//        }
//        return false;
//    }




    //    新添加一条菜单数据
    @Override
    @Transactional
    public SaResult saveOneService(SysMenuBso menuBso, MultipartFile icoPathFile, HttpServletRequest request) {
        String title = menuBso.getTitle();
        String titleTrim = StrUtil.trim(title);
        if (StrUtil.isBlank(titleTrim)){
            return SaResult.error("菜单标题不能为空");
        }
        if (menuBso.getPid()==null){
            return SaResult.error("pid不能为null");
        }

        if (icoPathFile==null){
            SysMenu sysMenu = BeanUtil.copyProperties(menuBso, SysMenu.class);
            save(sysMenu);
            return SaResult.ok("succeed");
        }

        String icoPath = fileOperation(icoPathFile, request);
        SysMenu sysMenu = BeanUtil.copyProperties(menuBso, SysMenu.class);
        sysMenu.setIcoPath(icoPath);
        save(sysMenu);
        return SaResult.ok("succeed");
    }

//   根据Pid,level值或者指定菜单项，但不做树型结构
    @Override
    public SaResult getChoseMenuListService(Integer pid,Integer level) {

//        1、获取登录用户id
        Object id = StpUtil.getLoginId();
        int loginId = Integer.parseInt(id.toString());
//        2、根据登陆者id，获取到登陆者的Location表中对应的仓库和项目
        SaResult locationList = locationService.getAllByUserIdService(loginId, 1, 100);
        Object data =locationList.getData();
//        把Object类型的data转换为LocationDto的Array列表，采用自定义的castList方式
        List<LocationDto>locationDtoList = castList(data, LocationDto.class);

//        创建两个列表容器
        List<Integer>warehouseIds=new ArrayList<>();
        List<Integer>projectIds=new ArrayList<>();

        for (LocationDto locationDto : locationDtoList) {
            if (locationDto.getProjectId()>0){
                projectIds.add(locationDto.getProjectId());
            }
            if (locationDto.getWarehouseId()>0){
                warehouseIds.add(locationDto.getWarehouseId());
            }
        }

        List<SysMenu> sysMenus = query().eq("level", level).eq("pid",pid).list();
//       如果warehouseIds是空，就把获取到的菜单数据去掉材料管理项
        if (warehouseIds.isEmpty()){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "材料管理"));
        }
//        如果projectIds是空，就把获取到的菜单数据去掉设备管理线
        if (projectIds.isEmpty()){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "设备管理"));
        }
//        获取登录用户的角色
        List<String> roleList = StpUtil.getRoleList();

        if (!new HashSet<>(roleList).containsAll(Arrays.asList("sys:admin","sys:manager","sys:coder","sys:superAdmin"))){
            sysMenus.removeIf(sysMenu -> Objects.equals(sysMenu.getTitle(), "后台管理"));
        }


        List<SysMenuBso> sysMenuBsoList = BeanUtil.copyToList(sysMenus, SysMenuBso.class);

        return SaResult.data(sysMenuBsoList);
    }

//    获取所有的菜单项，部分父子，不做树型结构
    @Override
    public SaResult getAllLineService(Integer current, Integer pageSize) {
        if (current==null||current==0){
            current=PAGE_CURRENT;
        }
        if (pageSize==null||pageSize==0){
            pageSize=PAGE_SIZE;
        }
        Page<SysMenu>page=new Page<>(current,pageSize);
        List<SysMenu> records = query().page(page).getRecords();
        List<SysMenuBso> sysMenuBsoList = BeanUtil.copyToList(records, SysMenuBso.class);
        return SaResult.data(sysMenuBsoList);
    }

//    根据Pid，获取pid下面一层的子菜单数据
    @Override
    public SaResult getChildrenService(Integer pid) {
        if (pid==null||pid<0){
            return SaResult.error("参数错误");
        }
        List<SysMenu> sysMenus = query().eq("pid", pid).list();
        List<SysMenuBso> sysMenuBsos = BeanUtil.copyToList(sysMenus, SysMenuBso.class);
        return SaResult.data(sysMenuBsos);
    }

//   修改一条菜单数据
    @Override
    @Transactional
    public SaResult modifyOneService(SysMenuBso menuBso, MultipartFile icoPathFile, HttpServletRequest request) {
//        todo 这几行没有必要留吧
//        SysMenu sysMenu = query().eq("id", menuBso.getId()).one();
//        if (BeanUtil.isEmpty(sysMenu)){
//            return SaResult.error("没有此用户，请核实！");
//        }
        String titleTrim = StrUtil.trim(menuBso.getTitle());
        if (StrUtil.isBlank(titleTrim)){
            menuBso.setTitle(null);
        }else {
            menuBso.setTitle(titleTrim);
        }
        String pathTrim = StrUtil.trim(menuBso.getPath());
        if (StrUtil.isBlank(pathTrim)){
            menuBso.setPath(null);
        }
        else {
            menuBso.setPath(pathTrim);
        }
        if (icoPathFile==null){
//            如果不修改图标文件，那么原图标地址也设置为null，不参与修改
            menuBso.setIcoPath(null);
            updateById(BeanUtil.copyProperties(menuBso,SysMenu.class));
            return SaResult.ok("modify succeed!");
        }
//        如果传进来新的图标文件，那么原图标文件的全文件地址不能为空
        String trimIcoPath = StrUtil.trim(menuBso.getIcoPath());
       if (StrUtil.isBlank(trimIcoPath)){
           return SaResult.error("原图片文件路径不能为空");
       }

//        获取到数据库中记录的图标文件的全网络名称：如:http://localhost:80/file/2024/04/01/menu_仓库---1.jpg
        String icoPath = menuBso.getIcoPath();
//        截取文件全网络名称中，/2024/04/01/menu_仓库---1.jpg这段字符串，以e为标志点截取
        String substring = icoPath.substring(icoPath.indexOf("e") + 2);
//        最终这个图片文件在服务器硬盘的实际地址是：fileLocation+substring,即e:/eq_ma_a/2024/04/01/menu_仓库---1.jpg
//        这里使用hutool中的FileUtil.del方法删除文件

        FileUtil.del(fileLocation + substring);
//        使用fileOperation方法把新文件存到服务器硬盘中
        String fileUrl = fileOperation(icoPathFile, request);
//        把获取到的全网路路径设定给menuBso对象中
        menuBso.setIcoPath(fileUrl);
        SysMenu sysMenuNew = BeanUtil.copyProperties(menuBso, SysMenu.class);
        updateById(sysMenuNew);
        return SaResult.ok("succeed!");

    }

//    删除一条菜单数据
    @Override
    @Transactional
    public SaResult deleteOneByIdService(Integer id) {
        SysMenu sysMenu = query().eq("id", id).one();
        if (sysMenu==null){
            return SaResult.error("没有改对象");
        }
//        获取到数据库中记录的图标文件的全网络名称：如:http://localhost:80/file/2024/04/01/menu_仓库---1.jpg
        String icoPath = sysMenu.getIcoPath();
        if (StrUtil.isBlank(StrUtil.trim(icoPath))){
            return SaResult.error("没有Ico");
        }
//        截取文件全网络名称中，/2024/04/01/menu_仓库---1.jpg这段字符串，以e为标志点截取
        String substring = icoPath.substring(icoPath.indexOf("e") + 2);
//        最终这个图片文件在服务器硬盘的实际地址是：fileLocation+substring,即e:/eq_ma_a/2024/04/01/menu_仓库---1.jpg
//        这里使用hutool中的FileUtil.del方法删除文件
        FileUtil.del(fileLocation + substring);
        removeById(id);
//        removeById(sysMenu);
        return SaResult.ok("succeed!");
    }



//    公共文件存储方法

    private String fileOperation(MultipartFile icoPathFile, HttpServletRequest request) {
//        获取文件的原始名称
        String originalFilename = icoPathFile.getOriginalFilename();
//        获取到最后一个“.”在图片文件名中的索引
        int i  = 0;
        if (originalFilename != null) {
            i = originalFilename.lastIndexOf(".");
        }
        String substring;
        String newFileName=null;
        if (originalFilename != null) {
//             把截取文件名的后缀  “.jpg”
            substring = originalFilename.substring(i);
            String upperCase = RandomUtil.randomString(5).toUpperCase(Locale.ROOT);

//            重新拼接文件名加.jpg，前缀是登录名，如登录名为eric，整照片的全名就是eric.jpg
//            为避免文件名出行由于菜单名称相同，而相同造成覆盖，采用了一个7位的随机字符串拼接
            newFileName="menu_"+ upperCase+substring;
        }
//        创建全路径名，不包括服务器的路径
        String realPath=fileLocation+menuIconPath;
        File folder=new File(realPath);
        String url;
        if (!folder.exists()){
            folder.mkdirs();
        }
        try {
            if (originalFilename!=null){
                icoPathFile.transferTo(new File(folder,newFileName));
            }
            /*request.getScheme():指服务器访问协议，如http
             * ://拼接http://
             * request>getServerName：服务器地址，如localhost
             * ":"拼接 ： 前面加起来就是http://localhost:
             * request.getServerPort:获取端口号，这里是80,拼接原来的就是http://localhost:80
             * /profilePhoto  就是在服务器硬盘上建立一个文件件，叫做profilePhoto,拼接后就是http://localhost:80/profilePhoto
             * format :就是那个日期 ，如/2024/03/19,拼接后就是http://localhost:80/profilePhoto/2024/03/19/
             * newFileName 存到硬盘文件的名字，如pikachu.jpg，拼接后就是http://localhost:80/profilePhoto/2024/03/19/pikachu.jpg
             * */
//            url=request.getScheme()+"://"+request.getServerName()+":" +request.getServerPort()+"/file"+menuIconPath+newFileName;
            url=menuIconPath+newFileName;

        }catch (IOException e){
            throw new RuntimeException(e);
        }
        return url;
    }

    private List<SysMenuDto> parseMenuTreeNew(List<SysMenuDto> sysMenuDtoList,int id) {


//        新建一个SysMenuDto对象作为容器
        List<SysMenuDto> result = new ArrayList<>();
//        1、获取第一级节点
        for (SysMenuDto menu : sysMenuDtoList) {
//            pid如果是null，那么就是第一层节点，然后把第一级节点对象放到result中
            if(Objects.equals(id, menu.getPid())) {
                result.add(menu);
            }
        }
//        2、调用recursiveTree方法递归获取子节点
//        对result列表各个元素遍历，同时执行递归函数recursiveTree
        for (SysMenuDto parent : result) {
            recursiveTreeNew(parent, sysMenuDtoList);
        }
        return result;
    }

    //    本方法配合getAllMenuService方法使用
    private void recursiveTreeNew(SysMenuDto parent, List<SysMenuDto> list) {
        for (SysMenuDto menu : list) {
            if(Objects.equals(parent.getId(),menu.getPid())) {
                recursiveTreeNew(menu, list);
                parent.getChildren().add(menu);
            }
        }
    }


    public static <T> List<T> castList(Object obj, Class<T> clazz)
    {
        List<T> result = new ArrayList<T>();
        if(obj instanceof List<?>)
        {
            for (Object o : (List<?>) obj)
            {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }
}
